热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

手枪|枪套_jQuery异步框架探究1:jQuery._Deferred方法

篇首语:本文由编程笔记#小编为大家整理,主要介绍了jQuery异步框架探究1:jQuery._Deferred方法相关的知识,希望对你有一定的参考价值。jQuery异步

篇首语:本文由编程笔记#小编为大家整理,主要介绍了jQuery异步框架探究1:jQuery._Deferred方法相关的知识,希望对你有一定的参考价值。



jQuery异步框架应用于jQuery数据缓存模块、jQuery ajax模块、jQuery事件绑定模块等多个模块,是jQuery的基础功能之一。实际上jQuery实现的异步回调机制可以看做java nio(不是aio)的近似,所以需要从更抽象层面的"异步回调"的视角分析解读该模块。这个部分与dom功能关系不大,是独立部分,可以看作是jQuery工具系列之一。



与异步框架相关的方法定义于jQuery类的静态方法中。只有三个方法,但是功能和应用及其强大!本篇详细讲解第一个方法jQuery._Deferred()。



_Deferred: function()
var // callbacks list
callbacks = [],
// stored [ context , args ]
fired,
// to avoid firing when already doing so
firing,
// flag to know if the deferred has been cancelled
cancelled,
// the deferred itself
deferred =
// done( f1, f2, ...)
done: function()
if ( !cancelled )
var args = arguments,
i,
length,
elem,
type,
_fired;
if ( fired )
_fired = fired;
fired = 0;

for ( i = 0, length = args.length; i elem = args[ i ];
type = jQuery.type( elem );
if ( type === "array" )
deferred.done.apply( deferred, elem );
else if ( type === "function" )
callbacks.push( elem );


if ( _fired )
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );


return this;
,
// resolve with given context and args
resolveWith: function( context, args )
if ( !cancelled && !fired && !firing )
// make sure args are available (#8421)
args = args || [];
firing = 1;
try
while( callbacks[ 0 ] )
callbacks.shift().apply( context, args );


finally
fired = [ context, args ];
firing = 0;


return this;
,
// resolve with this as context and given arguments
resolve: function()
deferred.resolveWith( this, arguments );
return this;
,
// Has this deferred been resolved?
isResolved: function()
return !!( firing || fired );
,
// Cancel
cancel: function()
cancelled = 1;
callbacks = [];
return this;

;
return deferred;
,


这是jQuery异步框架最基础的一个函数,真正对外暴露的Deferred函数依赖该函数的实现,从其名字可以看出,jQuery的设计者并不想使用者直接调用该方法,虽然这个方法已经很灵活已经可以应用到很多场景了,而且Javascript语言也没有语言层面上的机制阻止用户真的调用该方法。



分析其内部实现可以发现该函数设计为在被调用时并不需要传入任何参数,而是直接调用后得到一个异步对象:var deferred = jQuery._Deferred()。重点是这个返回的异步对象,这个对象有五个方法:done、resolveWith、resolve、isResolved、cancel,每个方法都值得深入分析。当然_Deferred函数里面还有四个变量:callbacks、fired、firing、cancelled,这四个变量也值得深入分析。




1 done方法



这个方法的实现是把传入的参数push到callbacks空数组中,以便后续resolveWith方法使用callbacks数组中的函数。



这个方法的参数只接受两种类型:要么是函数,要么是函数数组,其他类型不被处理。且传入函数数组时递归调用done方法自身继续把数组中的函数元素push到callbacks数组中。



配合resolveWith方法可以知道done方法的主要功能是存储将要执行的函数集合,resolveWith方法的功能是在指定的对象上根据指定的参数对象依次调用这个函数集合。因此,callbacks函数集合相当于是回调处理器集合,fired = [ context, args ]相当于是调用对象(包括其参数)。这样分离后带来的灵活性是惊人的--异步处理框架即将成型。顺便说一下,这个方法的名字done非常的不友好,对于熟悉回调模式的朋友们来说,其实这个方法名应该是addHandlers或者addCallbacks,甚至返回对象deferred(延迟)的名字都不如asynchor来得直观。



这个方法最后返回this关键字,即deferred对象自身,这样做的好处是可以链式多次调用,比如jQuery._Deferred().done(f1,f2).done([f3,f4,f5]),如果deferred对象的其他方法也这么干的话,那么也可以链式调用其他方法,而其他方法正是这么干的。



关于_fired变量和相关代码的作用在分析完resolveWith方法之后再看才会明朗。




2 resolveWith方法



resolveWith方法的实现是迭代回调函数集合callbacks中的函数于指定对象context和参数args之上执行--如果callbacks不为空的话,并且最后总是把指定对象context和参数args存入fired变量中--有可能还没有执行回调,如果callbacks为空。



resolveWith方法先判断回调函数集合callbacks是否有值"callbacks[ 0 ]",没有的话什么也不触发。



resolveWith方法最核心的是这一行:callbacks.shift().apply( context, args )。除了从回调函数集合callbacks中取出第一个函数元素调用外它还移除了这个元素,改变了callbacks数组,这样保证回调函数集合callbacks中的每个元素仅能执行唯一一次。



对于军事迷来说,有一个模型与这里的_Deferred函数极为类似,几乎再也找不到更贴切的类比了--手枪。


纵观_Deferred函数,回调函数数组callbacks相当于弹夹;


传递给done方法的函数参数相当于将要存储于弹夹的子弹,done方法主要职责是提供"压弹入夹"操作;


传递给resolveWith方法的参数( context, args )相当于手枪,resolveWith方法主要职责是执行"开枪"操作;


fired变量相当于枪套,存储传递给resolveWith方法的手枪[ context, args ];



现在回过头再看done方法,done方法是与resolveWith配合使用的,一般情况下既可以先上弹后开枪--先调用done方法后调用resolveWith方法,这种场景下手枪存在前就已经备有子弹了,因此直接在调用resolveWith方法时触发"开枪"操作;但是也可以先备枪后上弹射击--先调用resolveWith方法后调用done方法,这种场景在传递手枪的时候还没有子弹,提供子弹后才去触发射击动作--done方法内部调用resolveWith方法:



if ( fired )
_fired = fired;
fired = 0;

// ......
if ( _fired )
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );





除了前面两种操作外还有一种特殊操作:上弹-->开枪-->再上弹开枪(先调用done方法再调用resolveWith方法后再调用done方法),这个时候done方法装上新弹(传入新的函数参数push到回调函数数组)后可重复开枪(继续内部调用resolveWith方法),如果不换上新弹(不传入新的函数参数)就不能重复开枪(原回调函数数组经过resolveWith方法调用一次后已清空,它不可重复使用),这个操作时序完美的模拟手枪空仓挂机复位射击功能(子弹打光时阻止套筒回位,更换弹夹后可快速推弹入膛)!



这行代码"fired = 0;"的作用非常重要,正因为有这行代码才可以在done方法调用resolveWith方法重复开枪时生效(因为resolveWith方法内的第二个判断条件"if ( !cancelled && !fired && !firing )",我们前面说过fired变量表示枪套,怎么理解呢?关于枪套变量fired也需要重点解释,参见下面详细解释fired的部分。



我们再来看一个扩展的操作:jQuery._Deferred().done(f1,f2).resolveWith(context1, args1).done(f3).done([f4]).done(f5,[f6,f7]),说明空仓挂机复位射击的确是可重复的!这个世界上应该没有哪位枪械师会设计不能重复使用或只能重复使用一次的手枪吧?



关于这两个方法总结一下针对同一把枪的使用方法:


jQuery._Deferred().done(f1,f2).resolveWith(context1, args1)--先上弹后造枪,击发一次。


jQuery._Deferred().resolveWith(context1, args1).done(f1,f2)--先造枪后上弹,击发一次。


jQuery._Deferred().done(f1,f2).resolveWith(context1, args1).done(f3,f4).done([f5,f6],f7)--先上弹后造枪射击一次,再空仓挂机复位射击两次(可重复更多次射击)。



关于这两个方法即使做了总结之后也并没有完,注意前面的一个提示性描述:针对同一把枪,我们已经知道同一把枪可以重复使用不同的子弹,但是如果想针对不同的枪使用相同的子弹呢?这个需求是合理的--成员国众多的北约内部,标准9mm巴拉姆弹可不是只能应用于沙漠之鹰。



"针对不同的枪"说明需要调用resolveWith方法至少两次,第一次调用resolveWith方法传入的参数(context1, args1)代表第一把枪,第二次传入的参数(context2, args2)代表第二把枪,我们看看会发生什么情况:"jQuery._Deferred().done(f1,f2).resolveWith(context1, args1).resolveWith(context2,args2);"很遗憾,这个调用会歇菜!还是因为枪套变量fired的原因。变通一下呢:"jQuery._Deferred().done(f1,f2).resolveWith(context1, args1).done(f3).resolveWith(context2, args2)",还是不行!同样因为枪套变量fired的原因,resolveWith方法能且仅能调用一次,它不像done一样可以链式调用多次。



除了因为枪套变量fired的原因外,即使排除这个原因,"不同的枪使用相同的子弹"也是不能实现的,枪对子弹是一对多的关系,子弹对枪却是一对一的关系,即回调函数数组中的每一颗子弹被一只手枪击发后并不能被另一只枪再次击发,真正合理的需求描述是"不同型号的枪支应该可以使用相同规格的子弹"。



所以结论是--jQuery._Deferred()不能针对相同的子弹使用不同的手枪,它每次上弹后就认为只能匹配给第一次传入的手枪(或者说第一次造枪后就只能使用这种型号的这只手枪),如果要使用不同的手枪怎么办呢?--多次调用jQuery._Deferred()方法即可。




3 枪套--变量fired



首先需要熟悉的一个基础知识点是:done方法和resolveWith方法操作的变量fired是在函数调用对象链上一级的调用对象中,其他三个变量都是如此,这意味着deferred对象的不同方法可以共同操作它们。



resolveWith方法内有一行判断语句:"if ( !cancelled && !fired && !firing )",这三个变量任何一个都可能导致不能"开枪",关于firing和cancelled参见下面详细分析,这里分析fired变量。如果这个枪套不是有枪才能执行开枪操作,或者简单说只有这个枪套是空的才能执行开枪(其实同时受firing和cancelled约束):枪套不空说明里面存储手枪了,手枪没取出来当然不能开枪--实际上即使此时传入新的手枪进来也不能使用(jQuery._Deferred()对象比较重感情,它只使用第一次接触到的手枪,这也充分说明第一次是多么的重要。)!resolveWith方法在finally块中赋值给变量:"fired = [ context, args ];",相当于使用结束后将手枪插入手枪套,



再看done方法对fired变量的操作:"if ( fired ) _fired = fired;fired = 0;",枪套中有枪时,把它取出来存入当前调用对象域中的临时变量_fired,此时一定要清空枪套!否则下面调用resolveWith方法会失效(resolveWith方法会先检查枪套情况)。




4 射击指示器--变量firing



这个变量相当于"射击指示器",作用是当前正在射击时(调用resolveWith方法并通过了条件判断)给手枪标记一个状态--"正在射击"的状态("firing = 1;"),这个状态下的手枪不能再次扣动扳机开枪(即不能再次调用resolveWith方法),除非当前这次射击结束(在finally块中清除这个状态"firing = 0;")。



如果用手枪上的部件来类比的话,有点儿像"扳机连杆突起",作用是一样的:击发前扳机连杆钩住阻铁可以执行当前射击,击发后扳机连杆脱钩阻铁,同时套筒在后座作用下通过"扳机连杆突起"强迫扳机连杆在射击过程中不能自动回位钩着阻铁,直到子弹击发出去后套筒复进到位本次射击结束才放开扳机连杆,这样就保证了射击过程中不会重复执行击发动作。当然并不是所有手枪都通过设计"扳机连杆突起"完成射击指示功能的。



这个变量在实际执行环境中应该过于严格了,除非Javascript执行器采用的是多线程执行环境并且不同线程对这个变量的访问可能出现race condition,否则我认为单线程环境下应该不需要这个变量(也可能是我理解有误)。




5 全能保险--变量cancelled和cancel方法



cancelled变量非常类似手枪上的"击发保险"+"弹夹防跌落保险",如果不是需要调用cancel方法显式设置的话,那么它也可以类似"击针保险",总之它是一个全能保险。

关闭保险之后("cancelled = 1;"),resolveWith方法无法开枪了(判断条件不通过),就像击发保险被锁死一样。

关闭保险之后("cancelled = 1;"),done方法无法压弹入匣了(判断条件不通过),就像弹夹扣被锁死一样。

调用cancel方法在关闭保险的同时也清空弹夹了("callbacks = [];")。

总之调用cancel方法之后整个系统都不好了,大部分地方都不能继续正常工作了。



6 resolve方法



这个方法调用resolveWith方法,不需多说。



7 isResolved方法



只要射击指示器firing亮着或者枪套fired里有枪就认为已resolved。



总之,如果你是一个对手枪模型工作原理十分清楚的军事迷,那么读懂jQuery._Deferred源码和原理几乎是天然的。jQuery._Deferred只是jQuery异步框架的第一个最基础的部分就已经展开如此多的篇幅了,但是因为它是jQuery异步框架的核心,也是后两个方法的基础,这种展开是值得的。


最后郑重申明一点:未经许可,严禁转载!尊重他人劳动成果是获得对等尊重的前提。



推荐阅读
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • Ihavethefollowingonhtml我在html上有以下内容<html><head><scriptsrc..3003_Tes ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • Jquery 跨域问题
    为什么80%的码农都做不了架构师?JQuery1.2后getJSON方法支持跨域读取json数据,原理是利用一个叫做jsonp的概念。当然 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • position属性absolute与relative的区别和用法详解
    本文详细解读了CSS中的position属性absolute和relative的区别和用法。通过解释绝对定位和相对定位的含义,以及配合TOP、RIGHT、BOTTOM、LEFT进行定位的方式,说明了它们的特性和能够实现的效果。同时指出了在网页居中时使用Absolute可能会出错的原因,即以浏览器左上角为原始点进行定位,不会随着分辨率的变化而变化位置。最后总结了一些使用这两个属性的技巧。 ... [详细]
  • React基础篇一 - JSX语法扩展与使用
    本文介绍了React基础篇一中的JSX语法扩展与使用。JSX是一种JavaScript的语法扩展,用于描述React中的用户界面。文章详细介绍了在JSX中使用表达式的方法,并给出了一个示例代码。最后,提到了JSX在编译后会被转化为普通的JavaScript对象。 ... [详细]
  • 本文介绍了2015年九月八日的js学习总结及相关知识点,包括参考书《javaScript Dom编程的艺术》、js简史、Dom、DHTML、解释型程序设计和编译型程序设计等内容。同时还提到了最佳实践是将标签放到HTML文档的最后,并且对语句和注释的使用进行了说明。 ... [详细]
author-avatar
金牛梁浩_934
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有